package com.andova.net

import android.app.Dialog
import com.andova.component.AndovaComponentManager.getInstance
import com.andova.component.net.NetworkSusCallback
import com.andova.component.net.ResponseModel
import com.google.gson.Gson
import com.google.gson.JsonSyntaxException
import okhttp3.Request
import okio.Buffer
import retrofit2.Call
import retrofit2.Response
import java.io.IOException
import java.io.InputStream
import java.net.ConnectException
import java.net.SocketTimeoutException
import java.nio.charset.Charset

abstract class NetworkSusCallbackImpl<T : ResponseModel> : NetworkSusCallback<T> {
    private var page = 1
    fun call(call: Call<out ResponseModel>) = getInstance().netSusComponent().netSuspend().request(this@NetworkSusCallbackImpl, call)
    private fun stream(stream: InputStream): String {
        val buffer = ByteArray(128)
        var len: Int
        val sb = StringBuilder()
        try {
            while (stream.read(buffer).also { len = it } != -1) sb.append(String(buffer, 0, len))
            stream.close()
        } catch (e: IOException) {
        }
        return sb.toString()
    }

    private fun convertErrorBody(response: Response<*>): T? = response.body() as? T
    private fun <T> convertErrorBody(response: Response<*>, obj: Class<T>): T? {
        var `is`: InputStream? = null
        try {
            `is` = response.errorBody()?.byteStream() ?: return null
            val data = stream(`is`)
            return Gson().fromJson<T>(data, obj)
        } catch (e: Exception) {
        } finally {
            try {
                `is`?.close()
            } catch (e: IOException) {
                e.printStackTrace()
            }
        }
        return null
    }

    override suspend fun prepare() {
        if (!showDialog() || page != 1) return
        dialog()?.show()
    }

    override suspend fun error(obj: Any?) {
        if (obj !is Response<*>) return
        try {
            val body = convertErrorBody(obj)
            printLog("$obj, error message -> ${body?.message ?: "unknown"}", true)
            handle(body ?: return)
        } catch (e: ClassCastException) {
            printResponse(obj.raw())
        } catch (e: NullPointerException) {
            printResponse(obj.raw())
        }
    }

    override suspend fun success(respBody: T) {
        ++page
        handle(respBody)
    }

    override suspend fun response(vararg objects: Any?) {
        try {
            if (dialog() != null) dialog()?.dismiss()
        } catch (e: IllegalArgumentException) {
            e.printStackTrace()
        }
        for (obj in objects) {
            if (obj !is Response<*>) continue
            printRequest(obj.raw().request())
        }
    }

    override suspend fun failure(throwable: Throwable?, vararg objects: Any?) {
        try {
            if (dialog() != null) dialog()?.dismiss()
        } catch (e: IllegalArgumentException) {
            e.printStackTrace()
        }
        var req: String? = null
        for (obj in objects) {
            if (obj !is Call<*>) continue
            req = getRequest(obj.request())
        }
        when (throwable) {
            is JsonSyntaxException -> printLog("$req, error message -> 数据解析出错", true)
            is SocketTimeoutException -> printLog("$req, error message -> 连接超时", true)
            is ConnectException -> printLog("$req, error message -> 连接服务器异常", true)
            else -> printLog("$req, unknown throwable -> $throwable", true)
        }
    }

    private fun getRequest(request: Request): String? {
        try {
            val buffer = Buffer()
            request.body()?.writeTo(buffer)
            val charset = Charset.forName("UTF-8")
            val body = buffer.readString(charset)
            return if (body.isEmpty()) request.toString() else "$request\nthe request body is:$body"
        } catch (e: IOException) {
            printLog("the request body is:$request", true)
        } catch (e: NullPointerException) {
            printLog("the request body is:$request", true)
        }
        return null
    }

    private fun printRequest(request: Request) {
        if (!editLog()) return
        printLog(getRequest(request) ?: return)
    }

    private fun printResponse(response: okhttp3.Response) {
        if (!editLog()) return
        try {
            printLog(response.body()?.string() ?: return, true)
        } catch (e: NullPointerException) {
            printLog(e, true)
        } catch (e: IOException) {
            printLog(e, true)
        }
    }

    fun page(): Int = page
    fun reset() = run { page = 1 }
    open fun toast(): Boolean = false
    open fun dialog(): Dialog? = null
    open fun editLog(): Boolean = true
    open fun showDialog(): Boolean = false
    protected abstract suspend fun handle(respBody: T)
    protected abstract fun log(msg: Any, error: Boolean)
    private fun printLog(log: Any, error: Boolean = false) {
        if (!editLog()) return
        when (log) {
            is String -> log(log, error)
            is Throwable -> log(log, error)
        }
    }
}